<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>jsoop高级特征</title>
</head>
<body>
    <pre>
    对象本质可以理解为属性的集合,对象的属性包括数据属性和访问器属性
    数据属性:它包含的是一个数据值的位置,可以对数据值进行读写
    四大特征:1、value值2、writable表示是否可写,默认true,如果为false,则表示当前属性只读不可修改
    3、enumerable表示是否可枚举,默认true如果为false,则在for...in中不会被遍历
    4、configurable表示是否为可配置的,默认true,如果为false,则不能删除,而且不能再改回true
    访问器属性:这个属性不包含数据值的位置(get,set)
    访问器属性是专门保护数据属性的特殊属性,不实际存储数据
    分为getter和setter访问器    get方法负责读取变量值   set方法负责修改变量值
    四大特征:
    [[Configurable]]:表示能否通过delete删除属性从而重新定义属性，能否修改属性的特性、或者能否把属性修改为数据属性。默认值是true。

    [[Enumerable]]:表示能否通过for-in循环返回属性。默认值是true。
    
    [[Get]]:在读取属性时调用的函数。默认值是undefined。
    
    [[Set]]:在写入属性时调用的函数。默认值是undefined。
    </pre>
    <script>
        //严格模式
        /*
        不可使用with
        变量必须声明
        this默认是undefined而不是window
        为只读变量和不可扩展对象赋值会报错
        函数的形参不可以同名
        不可以使用caller和arguments的属性,会报错
        */
        'use strict';
        var o1={};
        o1.name="csdn";
        o1.age=18;
        o1.study=function(){
            alert("好好学习 天天向上");
        }
        //属性不能删除
        Object.defineProperty(o1,'age',{
            "configurable":false//修改成不可配置
        })
//        delete o1.age;//配置了configurable后删除属性严格模式会报错
//        console.log(o1);
        //不可逆的！！！
        /*
        Object.defineProperty(o1,'age',{
            "configurable":true//修改成可配置
        })
        */
//        delete o1.age;
//        console.log(o1);
        /*
        with(o1){   //严格模式下不可使用
            console.log(name);
            console.log(age);
        }
        */
            //方法this是谁???window undefined
            function test(a1,a){//严格模式下形参不可同名
                console.log(this);//严格模式下是undefined
                console.log(a1+a);
            }
            test(1,2);
        // arguments的使用
        //没写参数
        //arguments.callee==函数本身
        function mylist(){
        //  console.log(arguments.callee);//等于函数本身,严格模式下不可使用
            
            let n=arguments.length;//得到参数的个数
            for(var i=0;i<n;i++){
            console.log(arguments[i]);
            }
            
        }
        mylist('参数一号','参数二号','参数三号')
        //caller的使用指向函数的调用者
        function china(){
            hunan()
        }
        function hunan(){
            console.log("我代表湖南");
            console.log("调用者:"+hunan.caller);
        }
    //    china()   严格模式下报错caller
        //定义属性状态defineProperty(obj,属性名,{})
        Object.defineProperty(o1,'name',{
            "writable":false,//name属性不可修改
            "enumerable":false//name属性不可枚举
        })
        
        //修改属性---修改无效
    //    o1.name='碰磕'; //在严格模式下报错
        o1.age='17';
        //获取属性描述对象getOwnPropertyDescriptor(obj,属性名)
        var p1=Object.getOwnPropertyDescriptor(o1,'name');
        console.log(p1);
        var p2=Object.getOwnPropertyDescriptor(o1,'age');
        console.log(p2);
        console.log(o1.name);
        console.log(o1['name']);
        for(var at in o1){
            // at为属性名
            console.log(o1[at]);//得到所有属性的值
        }
    //    hero='碰磕';//未定义报错
        // console.log(hero);


        /*
         *访问器属性
        */
        var stu={
            id:1,
            name:'碰磕',
            salary:8000
        };
        Object.defineProperty(stu,'bank',{
            get:function(){
                console.log("get方法获取");
                return this.salary;
            },
            set:function(val){
                console.log("给stu赋值");
                this.salary+=val;   
            }
        })
        //测试
        stu.bank=1000;//自动调用set方法
        console.log(stu.bank);//自动调用get方法

        /*
            总结
            通过修改属性的四大特征来保护属性
            数据属性可以控制对象属性的只读,遍历或删除特征
            访问器属性可以通过自定义逻辑来设置和获取变量的值
        */


        /*
        以下作业在严格模式下完成：
1、创建一个猫对象,通过defineProperty给它定义name数据属性，值为大黄，不可修改，可枚举可配置，
color数据属性，值为黄色，可修改可枚举可配置，weight访问器属性，可枚举可配置。
2、创建一个学生stu对象，通过defineProperty定义一个score访问器属性，
当运行stu.score = 5;时打印出'成绩进步了5分'，当运行var s = stu.score;时打印出‘成绩是95分’
3、创建一个car对象，通过defineProperty定义一个price访问器属性，
当运行car.price = 9;时打印出'售车打9折'，当运行var p = car.price;时打印出‘价格是999999元’
        */
       //1
       var cat={
           "name":"大黄",
           "color":"黄色",
           "tz":10
       };
       Object.defineProperty(cat,'name',{
            "writable":false,//name属性不可修改
       })
       Object.defineProperty(cat,'weight',{
           get:function(){
                return this.tz;
           },
           set:function(val){
                return this.tz+=val;
           }
       })
       cat.weight=20;
       console.log(cat.weight);
       //2
       var stus={};
       let grads=90;
       Object.defineProperty(stus,'score',{
           get:function(){
               console.log("成绩是"+grads+"分");
                return grads;
           },
           set:function(val){
            console.log("成绩进步了"+val+"分");
            return grads+=val;
           }
       })
       stus.score=6;
       stus.score;
       //3
       var car={};
       let qian=1111110;
       Object.defineProperty(car,'price',{
            get:function(){
                console.log("价格是"+qian+"元");
                return qian;
            },
            set:function(val){
                console.log("售车打"+val+"折");
                return qian=qian*(val/10);
            }
       })
       car.price = 9;
       car.price;
    </script>
</body>
</html>